home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
C⁄C++
/
Marathon Map Viewer
/
@Source
/
myDocument.cpp
< prev
next >
Wrap
Text File
|
1995-06-06
|
17KB
|
425 lines
/*-----------------------------------------------------------------
Copyright ©1994 Steve Israelson
This file contains the code for the "myDocument" class.
-----------------------------------------------------------------*/
#include "myDocument.h"
#include "myApplication.h"
#include "commandNumbers.h"
#include "chunkStorage.h"
#include "levelView.h"
#include "levelUtils.h"
#include <String_Utils.h>
#include <String.h>
/*-----------------------------------------------------------------
Create a new editor document instance. If no file is passed
in then we create a new blank document. This just initializes
our instance variables to a known state, puts up a window
to display the level names in, and then opens the file
and displays the level names.
-----------------------------------------------------------------*/
myDocument::myDocument(LCommander *inSuper, FSSpec *inFileSpec) : LSingleDoc(inSuper)
{
// zero out the header. setmem?
for (int x = 0; x < sizeof(mapHeader); ++x)
((char*)(&theMapFile.theMapHeader))[x] = 0;
// set up the map file for a new file
strcpy(theMapFile.theMapHeader.name, "Untitled");
theMapFile.theMapHeader.numLevels = 0;
theMapFile.theMapHeader.type = 0;
theMapFile.theMapHeader.flags = 0;
theMapFile.theMapHeader.mapSize = 0;
theMapFile.theLevelInfo = nil;
theMapFile.theLevelData = nil;
levelWindows = nil;
// create the level list window
mWindow = LWindow::CreateWindow(1000, this);
if (mWindow)
{
LListBox *theList = (LListBox*) mWindow->FindPaneByID(10000);
theList->AddListener(this);
}
// open the file if it is passed in
if (inFileSpec == nil)
NameNewDoc(); // Set name of untitled window
else
OpenFile(*inFileSpec); // Display contents of file in window
}
/*-----------------------------------------------------------------
When we have been disposed of we should close any level windows
that were opened and dispose of any memory we allocated.
Also close this document. It should have been saved by now.
-----------------------------------------------------------------*/
myDocument::~myDocument()
{
if (theMapFile.theLevelInfo)
DisposePtr((Ptr)theMapFile.theLevelInfo);
for (int x = 0; x < theMapFile.theMapHeader.numLevels; ++x)
{
if (levelWindows[x])
{
delete levelWindows[x]; // close all windows with levels showing in them
levelWindows[x] = nil;
}
if (theMapFile.theLevelData[x])
{
if (theMapFile.theLevelData[x]->thePoints) DisposePtr((Ptr)theMapFile.theLevelData[x]->thePoints);
if (theMapFile.theLevelData[x]->theExtendedPts) DisposePtr((Ptr)theMapFile.theLevelData[x]->theExtendedPts);
if (theMapFile.theLevelData[x]->theLines) DisposePtr((Ptr)theMapFile.theLevelData[x]->theLines);
if (theMapFile.theLevelData[x]->theSides) DisposePtr((Ptr)theMapFile.theLevelData[x]->theSides);
if (theMapFile.theLevelData[x]->thePolys) DisposePtr((Ptr)theMapFile.theLevelData[x]->thePolys);
if (theMapFile.theLevelData[x]->theLites) DisposePtr((Ptr)theMapFile.theLevelData[x]->theLites);
if (theMapFile.theLevelData[x]->theObjects) DisposePtr((Ptr)theMapFile.theLevelData[x]->theObjects);
if (theMapFile.theLevelData[x]->theMissionInfo) DisposePtr((Ptr)theMapFile.theLevelData[x]->theMissionInfo);
if (theMapFile.theLevelData[x]->thePlatforms) DisposePtr((Ptr)theMapFile.theLevelData[x]->thePlatforms);
if (theMapFile.theLevelData[x]->theiidxs) DisposePtr((Ptr)theMapFile.theLevelData[x]->theiidxs);
if (theMapFile.theLevelData[x]->theNOTEs) DisposePtr((Ptr)theMapFile.theLevelData[x]->theNOTEs);
if (theMapFile.theLevelData[x]->theplacs) DisposePtr((Ptr)theMapFile.theLevelData[x]->theplacs);
if (theMapFile.theLevelData[x]->levelAuthor) DisposePtr((Ptr)theMapFile.theLevelData[x]->levelAuthor);
DisposePtr((Ptr)theMapFile.theLevelData[x]);
}
}
if (theMapFile.theLevelData)
DisposePtr((Ptr)theMapFile.theLevelData);
if (levelWindows)
DisposePtr((Ptr)levelWindows);
}
/*-----------------------------------------------------------------
We do not need to name a document in this case.
This would be called if we let the user create new blank
documents, in which case we would name the document, "untitled"
-----------------------------------------------------------------*/
void myDocument::NameNewDoc()
{
return; // our palette always has the name of map file.
}
/*-----------------------------------------------------------------
Open the map file and read some info out of it.
We need the names of the levels so we can show them to the user
in our document window. This window has a listbox in it
that we fill with the names of all the levels we find.
-----------------------------------------------------------------*/
void myDocument::OpenFile(FSSpec &inFileSpec)
{
Try_ {
mFile = new LFile(inFileSpec);
mFile->OpenDataFork(fsRdWrPerm); // the map data is stored in the data fork
short fileRefNum = mFile->GetDataForkRefNum();
long count;
// read in the map header
count = sizeof(mapHeader);
ThrowIfOSErr_(FSRead(fileRefNum, &count, &theMapFile.theMapHeader));
// create the level window list
levelWindows = (LWindow**)NewPtrClear(sizeof(LWindow *) * theMapFile.theMapHeader.numLevels);
// create and read in the level info. It is stored at the end of the file
count = sizeof(levelInfo) * theMapFile.theMapHeader.numLevels;
theMapFile.theLevelInfo = (levelInfo*)NewPtrClear(count);
SetFPos(fileRefNum, fsFromLEOF, -count);
ThrowIfOSErr_(FSRead(fileRefNum, &count, theMapFile.theLevelInfo));
// create the level info data
theMapFile.theLevelData = (levelData**)NewPtrClear(sizeof(levelData*) * theMapFile.theMapHeader.numLevels);
for (int i = 0; i < theMapFile.theMapHeader.numLevels; ++i)
{
theMapFile.theLevelData[i] = (levelData*)NewPtrClear(sizeof(levelData));
// someone will want to talk to us, so save a pointer to us!
theMapFile.theLevelData[i]->ownerDocument = (Ptr)this;
}
// read in the name chunks for each level
// actually only reads in the mission info at this time
readNameChunks(fileRefNum);
// when edited the level data itself will be read in
// fill the level names window with the names of all the levels read in.
setNameList();
// set the name of the window to the name of the map file
mWindow->SetDescriptor(inFileSpec.name);
mIsSpecified = true;
}
Catch_(inErr) { // we got an error reading in the file
delete this;
Throw_(inErr);
} EndCatch_
}
/*-----------------------------------------------------------------
Fills the name list with the names from the levels.
Just gets a pointer to the listbox and then asks it
for a ListHandle. The we use the Mac ListManager to
fill in the entrys for the names of the levels.
We delete the old names, so if the user adds or changes a level, then
we can call this to re-build the level name list.
-----------------------------------------------------------------*/
void myDocument::setNameList(void)
{
LListBox *theList = (LListBox*) mWindow->FindPaneByID(10000);
ListHandle theMacList = theList->GetMacListH();
LDoDraw(false, theMacList); // turn drawing off for speed
LDelColumn(0, 0, theMacList); // deletes all columns
LDelRow(0, 0, theMacList); // deletes all rows
LAddColumn(1, 10000, theMacList); // add one at end
for (int x = 0; x < theMapFile.theMapHeader.numLevels; ++x)
{
Point theCell = {0,0};
theCell.v = LAddRow(1, 10000, theMacList);
LSetCell(theMapFile.theLevelData[x]->theMissionInfo[0].levelName, 64, theCell, theMacList);
}
LDoDraw(true, theMacList);
theList->Refresh(); // redraw entire list
}
/*-----------------------------------------------------------------
Reads in the mission info chunks, so we can get the names
of the levels.
-----------------------------------------------------------------*/
void myDocument::readNameChunks(short fileRefNum)
{
Ptr chunkData;
for (int x = 0; x < theMapFile.theMapHeader.numLevels; ++x)
{
// actually read the chunk in.
chunkData = readChunk(fileRefNum, 'Minf', theMapFile.theLevelInfo, x);
if (chunkData)
{
theMapFile.theLevelData[x]->numMinfos = GetPtrSize(chunkData) / sizeof(Minfdata);
theMapFile.theLevelData[x]->theMissionInfo = (Minfdata*)chunkData;
// this fixes the level name string so it will work with the list manager
Boolean clear = false;
for (short i = 0; i < 64; ++i) // clear remaining parts of string to 0 since the list manager displays the entire string
{
if (!theMapFile.theLevelData[x]->theMissionInfo[0].levelName[i])
clear = true;
if (clear)
theMapFile.theLevelData[x]->theMissionInfo[0].levelName[i] = 0;
}
}
}
}
/*-----------------------------------------------------------------
If you want to save a map file, do it here. Good luck! Its hard.
-----------------------------------------------------------------*/
void myDocument::DoSave()
{
}
/*-----------------------------------------------------------------
Has the user changed the file? If so Powerplant will ask the
user if it should be saved.
-----------------------------------------------------------------*/
Boolean myDocument::IsModified()
{
return mIsModified;
}
/*-----------------------------------------------------------------
Called for save as, or when saving a new document.
-----------------------------------------------------------------*/
void myDocument::DoAESave(FSSpec &inFileSpec, OSType inFileType)
{
}
/*-----------------------------------------------------------------
When a window we created is closed, this is called.
We update the window list for the level windows.
-----------------------------------------------------------------*/
Boolean myDocument::AllowSubRemoval(LCommander *inSub)
{
// need to loop through the levels array and set the appropriate entry to nil
if (levelWindows)
{
for (int x = 0; x < theMapFile.theMapHeader.numLevels; ++x)
if (levelWindows[x] == inSub)
levelWindows[x] = 0; // closed a level window
}
return LSingleDoc::AllowSubRemoval(inSub);
}
/*-----------------------------------------------------------------
The user wants to revert changes. Should re-load the level from disk.
-----------------------------------------------------------------*/
void myDocument::DoRevert()
{
}
/*-----------------------------------------------------------------
-----------------------------------------------------------------*/
void myDocument::DoPrint()
{
}
/*-----------------------------------------------------------------
The user double clicked on a level name, so lets open it in a
window. This is when the level actually gets read into ram.
-----------------------------------------------------------------*/
void myDocument::editLevel(short theLevelNum)
{
if (theLevelNum >= theMapFile.theMapHeader.numLevels || theMapFile.theLevelData[theLevelNum]->deleteMe)
{
SysBeep(1);
return;
}
if (levelWindows[theLevelNum]) // already open.
{
levelWindows[theLevelNum]->Select(); // so bring it to the front
return;
}
// need to read in the data for the level
readLevel(theLevelNum);
// create a window to show the level. A levelView is created by powerplant
levelWindows[theLevelNum] = LWindow::CreateWindow(1001, this);
// could re-position the window here
levelWindows[theLevelNum]->Show();
// set the name of the window to the name of the level
CtoPstr((char*)theMapFile.theLevelData[theLevelNum]->theMissionInfo[0].levelName);
levelWindows[theLevelNum]->SetDescriptor((StringPtr)theMapFile.theLevelData[theLevelNum]->theMissionInfo[0].levelName);
PtoCstr((StringPtr)theMapFile.theLevelData[theLevelNum]->theMissionInfo[0].levelName);
// need to find the pane and set the level data for it
levelView *theLevelView = (levelView*) levelWindows[theLevelNum]->FindPaneByID(10000);
levelWindows[theLevelNum]->SetLatentSub(theLevelView);
// give the levelView the levelData so it can do its thing
theLevelView->setLevelData(theMapFile.theLevelData[theLevelNum]);
}
/*-----------------------------------------------------------------
Read the level into ram. This does not assume any particular
order of the chunks in the file, as per the specs.
-----------------------------------------------------------------*/
void myDocument::readLevel(short theLevelNum)
{
theMapFile.theLevelData[theLevelNum]->theMapHeader = &theMapFile.theMapHeader;
if (mFile && !theMapFile.theLevelData[theLevelNum]->readIn) // are we reading from a file or creating one?
{
short fileRefNum = mFile->GetDataForkRefNum();
Ptr chunkData;
theMapFile.theLevelData[theLevelNum]->readIn = true;
// read in 'PNTS', 'LINS', 'SIDS', 'POLY', 'LITE', 'OBJS' etc.
chunkData = readChunk(fileRefNum, 'NAME', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numNames = GetPtrSize(chunkData) / sizeof(levelName);
theMapFile.theLevelData[theLevelNum]->levelAuthor = (levelName*)chunkData;
}
chunkData = readChunk(fileRefNum, 'PNTS', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numPoints = GetPtrSize(chunkData) / sizeof(PNTSdata);
theMapFile.theLevelData[theLevelNum]->thePoints = (PNTSdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'EPNT', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numExtendedPts = GetPtrSize(chunkData) / sizeof(EPNTdata);
theMapFile.theLevelData[theLevelNum]->theExtendedPts = (EPNTdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'LINS', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numLines = GetPtrSize(chunkData) / sizeof(LINSdata);
theMapFile.theLevelData[theLevelNum]->theLines = (LINSdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'SIDS', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numSides = GetPtrSize(chunkData) / sizeof(SIDSdata);
theMapFile.theLevelData[theLevelNum]->theSides = (SIDSdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'POLY', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numPolys = GetPtrSize(chunkData) / sizeof(POLYdata);
theMapFile.theLevelData[theLevelNum]->thePolys = (POLYdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'LITE', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numLites = GetPtrSize(chunkData) / sizeof(LITEdata);
theMapFile.theLevelData[theLevelNum]->theLites = (LITEdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'NOTE', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numNOTEs = GetPtrSize(chunkData) / sizeof(NOTEdata);
theMapFile.theLevelData[theLevelNum]->theNOTEs = (NOTEdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'OBJS', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numObjects = GetPtrSize(chunkData) / sizeof(OBJSdata);
theMapFile.theLevelData[theLevelNum]->theObjects = (OBJSdata*)chunkData;
}
chunkData = readChunk(fileRefNum, 'plac', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numplacs = GetPtrSize(chunkData) / sizeof(placData);
theMapFile.theLevelData[theLevelNum]->theplacs = (placData*)chunkData;
}
chunkData = readChunk(fileRefNum, 'plat', theMapFile.theLevelInfo, theLevelNum);
if (chunkData)
{
theMapFile.theLevelData[theLevelNum]->numPlatforms = GetPtrSize(chunkData) / sizeof(platdata);
theMapFile.theLevelData[theLevelNum]->thePlatforms = (platdata*)chunkData;
}
// bungie says do not use EPNTs
convertEPNTstoPNTS(theMapFile.theLevelData[theLevelNum]);
}
else
{ // create new blank level here
}
}
/*-----------------------------------------------------------------
Handle the user double clicking on the level name.
-----------------------------------------------------------------*/
void myDocument::ListenToMessage(MessageT inMessage, void *ioParam)
{
switch (inMessage)
{
case 1000: // a level name was double clicked
LListBox *theList = (LListBox*) mWindow->FindPaneByID(10000);
ListHandle theMacList = theList->GetMacListH();
Point theCell = {0,0};
if (LGetSelect(true, &theCell, theMacList))
{
if (LGrowZone::GetGrowZone()->MemoryIsLow()) // don't let user get into trouble
SysBeep(1);
else
editLevel(theCell.v); // actually edit the level
}
break;
}
}